﻿using System;
using System.IO;
using BitButterCORE.Object;
using BitButterCORE.Extension;

namespace BitButterCORE.Factory
{
	public abstract class PersistentFactory<THeader, TObject, TFactory> : BaseFactory<TObject, TFactory>
		where THeader : BasePersistentHeader
		where TObject : BasePersistentObject<THeader>
		where TFactory : PersistentFactory<THeader, TObject, TFactory>
	{
		protected PersistentFactory()
		{
			LoadPersistentObjectsFromFilesUnderRootDirectory();
		}

		protected virtual void LoadPersistentObjectsFromFilesUnderRootDirectory()
		{
			var root = GetRootDirectory();

			if (root.Exists)
			{
				foreach (var file in root.GetFiles())
				{
					if (TryParseFilename(file.Name, out var fileID))
					{
						Load(fileID);
					}
				}
			}
		}

		byte[] LoadContentFromFile(string filepath)
		{
			var result = default(byte[]);
			try
			{
				result = File.ReadAllBytes(filepath);
			}
			catch (Exception)
			{
				// do nothing for now
				// ...
			}
			return result;
		}

		void OverwriteContentToFile(string filepath, byte[] content)
		{
			try
			{
				File.WriteAllBytes(filepath, content);
			}
			catch (Exception)
			{
				// do nothing for now
				// ...
			}
		}

		public virtual void Load(uint id)
		{
			var persistentObject = this[id];
			var content = LoadContentFromFile(GetFilepathForID(id));
			if (content != null)
			{
				persistentObject.InitializePersistentObject(content);
			}
		}

		public virtual void Save(uint id)
		{
			var persistentObject = this[id];
			if (persistentObject.Data != null)
			{
				OverwriteContentToFile(GetFilepathForID(id), persistentObject.Header.Data.Append(persistentObject.Data));
			}
		}

		public override TObject this[uint id]
		{
			get
			{
				var result = base[id];
				if (result == null)
				{
					result = (TObject)Activator.CreateInstance(typeof(TObject), GetFilepathForID(id));
					Factory.Add(id, result);
					result.ID = id;
				}
				return result;
			}
		}

		DirectoryInfo GetRootDirectory()
		{
			var result = new DirectoryInfo(RootPath);
			if (!result.Exists)
			{
				try
				{
					Directory.CreateDirectory(RootPath);
				}
				catch (Exception)
				{
					// do nothing for now
					// ...
				}
			}
			return result;
		}

		string GetFilepathForID(uint id)
		{
			return Path.Combine(RootPath, string.Format("{0}_{1}.{2}", PersistentFilePrefix, id.ToString(), PersistentFileExtension));
		}

		bool TryParseFilename(string fullname, out uint id)
		{
			// Assume all files have format: (Prefix)_(id).(ext)
			var result = false;
			id = 0;

			var extension = fullname.LastIndexOf(PersistentFileExtension);
			if (extension > 0)
			{
				var filename = fullname.Substring(0, extension - 1);
				var splitFilename = filename.Split('_');
				if (splitFilename.Length == 2 && splitFilename[0] == PersistentFilePrefix)
				{
					result = uint.TryParse(splitFilename[1], out id);
				}
			}
			return result;
		}

		protected abstract string RootPath { get;}

		protected virtual string PersistentFilePrefix => "Save";

		protected virtual string PersistentFileExtension => "sav";
	}
}
